import Watcher from "../watcher";

export default class Compiler {
  constructor(vm) {
    this.vm = vm;
    this.methods = vm.$methods;
    this.compile(vm.$el);
  }

  compile(el) {
    const childNodes = el.childNodes;
    Array.from(childNodes).forEach((node) => {
      if (this.isTextNode(node)) {
        // 文本节点: 模板字符串中使用
        this.compileText(node);
      } else if (this.isElementNode(node)) {
        // 元素节点
        this.compileElement(node);
      }

      // 有子节点，递归调用
      if ((node, childNodes && node, childNodes.length > 0)) {
        this.compile(node);
      }
    });
  }

  compileText(node) {
    // {{msg}} msg: hello Vue
    const reg = /\{\{(.+?)\}\}/;
    const value = node.texContent; // hello Vue

    if (reg.test(value)) {
      const key = RegExp.$1.trim(); // msg
      node.texContent = value.replace(reg, this.vm[key]);

      new Watcher(this.vm, key, (newVal) => {
        node.texContent = newVal;
      });
    }
  }

  compileElement(node) {
    if (node.attributes.length) {
      Array.from(node.attributes).forEach((attr) => {
        // 遍历元素节点的所有属性
        if (this.isDirective(attr)) {
          if (attr === "v-text") {
            this.textUpdater(
              node,
              this.vm[node.attributes[attr]],
              node.attributes[attr]
            );
          } else if (attr === "v-html") {
            this.htmlUpdater(
              node,
              this.vm[node.attributes[attr]],
              node.attributes[attr]
            );
          } else if (attr === "v-model") {
            this.modelUpdater();
          } else if (attr === "v-on") {
            clickUpdater(node, null, null, attr.subStr(2));
          }
          let key = node.attributes[attr];
          this.update(node, key, vm[key]);
        }
      });
    }
  }

  update(node, key, directivename) {
    // v-model,v-text,v-html,v-on:click
    const updateFn = this[directivename + "Updater"];
    updateFn && updateFn.call(this, this.vm, this.vm[key], key, directivename);
  }

  /** 解析v-text */
  textUpdater(node, value, key) {
    node.texContent = value;
    new Watcher(this.vm, key, (newVal) => {
      node.texContent = newVal;
    });
  }

  /** 解析v-model */
  modelUpdater(node, value, key) {
    node.value = value;
    new Watcher(this.vm, key, (newVal) => {
      node.value = newVal;
    });

    node.addEventListener("input", () => {
      this.vm[key] = node.value;
    });
  }

  htmlUpdater(node, value, key) {
    node.innerHtml = value;
    new Watcher(this.vm, key, (newVal) => {
      node.innerHtml = newVal;
    });
  }

  clickUpdater(node, value, key, directivename) {
    node.addEventListener(directivename, this.methods[key]);
  }

  /** 判断是否是文本节点 */
  isTextNode(node) {
    return node.nodeType === 3;
  }

  /** 判断是否是html元素节点 */
  isElementNode(node) {
    return node.nodeType === 1;
  }

  /** 判断是否是Vue指令 */
  isDirective(attrName) {
    // v-
    return attrName.startsWith("v-");
  }
}
